package app.modules.menu.business.service;

import app.modules.common.constant.CommonLockConstants;
import app.modules.common.enums.ShowFlagEnum;
import app.modules.menu.constant.MenuLockConstants;
import app.modules.menu.constant.MenuMsgConstants;
import app.modules.menu.dto.dal.MenuDetailDalDTO;
import app.modules.menu.dto.web.menu.WebMenuDTO;
import app.modules.menu.dto.web.menu.WebMenuSaveDTO;
import app.modules.menu.dto.web.menu.WebMenuUpdateDTO;
import app.modules.menu.persistence.dao.MenuDAO;
import app.modules.menu.persistence.dao.MenuFunctionRelationDAO;
import app.modules.menu.util.conversion.MenuConvertUtil;
import jasmine.framework.common.exception.BusinessException;
import jasmine.framework.common.exception.DataNotFoundException;
import jasmine.framework.common.util.CheckUtil;
import jasmine.framework.common.util.I18nUtil;
import jasmine.framework.common.util.MapperUtil;
import jasmine.framework.common.util.NewUtil;
import jasmine.framework.lock.annotation.DistributedLock;
import jasmine.security.rbac.model.SecMenu;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;

/**
 * @author mh.z
 */
@Service
public class MenuService {
    private MenuDAO menuDAO;
    private MenuFunctionRelationDAO menuFunctionRelationDAO;

    public MenuService(MenuDAO menuDAO, MenuFunctionRelationDAO menuFunctionRelationDAO) {
        this.menuDAO = menuDAO;
        this.menuFunctionRelationDAO = menuFunctionRelationDAO;
    }

    /**
     * 导入数据
     *
     * @param menus
     */
    @Transactional(rollbackFor = Exception.class)
    public void importMenus(List<SecMenu> menus) {
        CheckUtil.notNull(menus, "menus null");

        menuDAO.saveBatch(menus);
    }

    /**
     * 保存菜单
     *
     * @param saveDTO
     * @return
     */
    @DistributedLock(category = MenuLockConstants.LOCK_CATEGORY_CRUD_MENU, key = CommonLockConstants.LOCK_KEY_TENANT)
    @Transactional(rollbackFor = Exception.class)
    public WebMenuDTO saveMenu(WebMenuSaveDTO saveDTO) {
        CheckUtil.notNull(saveDTO, "saveDTO null");
        Long parentId = saveDTO.getParentId();

        String menuCode = saveDTO.getMenuCode();
        long menuCodeCount = menuDAO.countByMenuCode(menuCode, null);
        if (menuCodeCount > 0) {
            throw new BusinessException(MenuMsgConstants.THE_MENU_CODE_ALREADY_EXISTS,
                    NewUtil.array(menuCode));
        }

        if (parentId != null) {
            SecMenu parentMenu = menuDAO.getById(parentId);
            if (parentMenu == null) {
                throw new DataNotFoundException(SecMenu.class, parentId);
            }
        }

        SecMenu secMenu = MenuConvertUtil.webMenuSaveDTO2SecMenu(saveDTO);
        menuDAO.save(secMenu);

        return getWebMenuDtoById(secMenu.getId());
    }

    /**
     * 更新菜单
     *
     * @param updateDTO
     * @return
     */
    @DistributedLock(category = MenuLockConstants.LOCK_CATEGORY_CRUD_MENU, key = CommonLockConstants.LOCK_KEY_TENANT)
    @Transactional(rollbackFor = Exception.class)
    public WebMenuDTO updateMenu(WebMenuUpdateDTO updateDTO) {
        CheckUtil.notNull(updateDTO, "updateDTO null");
        Long id = CheckUtil.notNull(updateDTO.getId(), "updateDTO.id null");
        Long parentId = updateDTO.getParentId();

        String menuCode = updateDTO.getMenuCode();
        long menuCodeCount = menuDAO.countByMenuCode(menuCode, Collections.singletonList(id));
        if (menuCodeCount > 0) {
            throw new BusinessException(MenuMsgConstants.THE_MENU_CODE_ALREADY_EXISTS,
                    NewUtil.array(menuCode));
        }

        if (parentId != null) {
            SecMenu parentMenu = menuDAO.getById(parentId);
            if (parentMenu == null) {
                throw new DataNotFoundException(SecMenu.class, parentId);
            }
        }

        SecMenu secMenu = menuDAO.getById(id);
        if (secMenu == null) {
            throw new DataNotFoundException(SecMenu.class, id);
        }

        MapperUtil.mapFields(updateDTO, secMenu);
        menuDAO.strictUpdateById(secMenu);

        return getWebMenuDtoById(id);
    }

    /**
     * 删除菜单
     *
     * @param id
     */
    @DistributedLock(category = MenuLockConstants.LOCK_CATEGORY_CRUD_MENU, key = CommonLockConstants.LOCK_KEY_TENANT)
    @Transactional(rollbackFor = Exception.class)
    public void deleteMenuById(Long id) {
        CheckUtil.notNull(id, "id null");

        long childMenuCount = menuDAO.countChildMenus(id);
        if (childMenuCount > 0) {
            throw new BusinessException(MenuMsgConstants.CANNOT_DELETE_BECAUSE_HAS_CHILD_MENUS, null);
        }

        menuDAO.deleteById(id);
        menuFunctionRelationDAO.deleteByMenuId(id);
    }

    /**
     * 查询菜单
     *
     * @param id
     * @return
     */
    public WebMenuDTO getWebMenuDtoById(Long id) {
        MenuDetailDalDTO menuDetailDalDTO = menuDAO.getMenuDetailDalDtoById(id);

        if (menuDetailDalDTO == null) {
            return null;
        }

        WebMenuDTO webMenuDTO = MenuConvertUtil.menuDetailDalDTO2WebMenuDTO(menuDetailDalDTO);
        populateWebMenuDTO(webMenuDTO);

        return webMenuDTO;
    }

    /**
     * 填充数据
     *
     * @param record
     */
    public void populateWebMenuDTO(WebMenuDTO record) {
        if (record == null) {
            return;
        }

        ShowFlagEnum showFlagEnum = ShowFlagEnum.parseValue(record.getShowFlag());
        record.setShowFlagDesc(I18nUtil.getMessage(showFlagEnum));
    }

}
